Skip to content

Devops/#12 GitHub Actions 기반 CI/CD 테스트#13

Merged
jin2304 merged 10 commits intodevfrom
devops/#12
Dec 8, 2025
Merged

Devops/#12 GitHub Actions 기반 CI/CD 테스트#13
jin2304 merged 10 commits intodevfrom
devops/#12

Conversation

@jin2304
Copy link
Copy Markdown
Member

@jin2304 jin2304 commented Nov 23, 2025

💡 이슈

resolve {#12}

🤩 개요

PR의 개요를 적어주세요.

🧑‍💻 작업 사항

작업한 내용을 적어주세요.

📖 참고 사항

공유할 내용, 레퍼런스, 추가로 발생할 것으로 예상되는 이슈, 스크린샷 등을 넣어 주세요.

Summary by CodeRabbit

릴리스 노트

  • Chores
    • GitHub Actions 기반 CI/CD 워크플로우 추가로 자동 빌드 및 배포 설정
    • Docker 컨테이너 베이스 이미지 최적화
    • 프로젝트 버전 파일 추가 (1.1.0)

✏️ Tip: You can customize this high-level summary in your review settings.

@jin2304 jin2304 self-assigned this Nov 23, 2025
@jin2304 jin2304 added the 🌎 deploy 배포 관련 작업 label Nov 23, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 23, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

개요

이 PR은 GitHub Actions 워크플로우를 추가하여 버전 기반 자동 빌드 및 배포 파이프라인을 구축합니다. 변경사항에는 version.txt 파일 신규 추가, Dockerfile의 기본 이미지 최적화(OpenJDK에서 Eclipse Temurin Alpine으로 변경), 그리고 Docker Hub 로그인, 이미지 빌드/푸시, SSH를 통한 원격 배포를 수행하는 CI/CD 워크플로우 구성이 포함됩니다.

시퀀스 다이어그램

sequenceDiagram
    participant GitHub as GitHub
    participant Build as build-and-push Job
    participant DockerHub as Docker Hub
    participant Server as Remote Server

    GitHub->>Build: Trigger workflow (push/PR to main/dev)
    Build->>Build: Checkout repo
    Build->>Build: Read version.txt → IMAGE_TAG
    Build->>Build: Setup JDK 17, build JAR
    Build->>DockerHub: Login
    Build->>Build: Build Docker image (tag: version & latest)
    Build->>DockerHub: Push images
    
    Note over Build,Server: Deploy Phase
    Build->>Server: SSH connection
    Server->>DockerHub: Login & pull images
    Server->>Server: Stop/remove existing container
    Server->>Server: Run new container (port 8080, env file)
    Server-->>GitHub: Deployment complete
Loading

예상 코드 리뷰 소요 시간

🎯 3 (중간) | ⏱️ ~20분

  • 특별히 검토 필요한 영역:
    • .github/workflows/deploy-test.yml: Docker Hub 인증정보 관리 및 원격 SSH 배포 보안 설정 검증 필수
    • Dockerfile: Eclipse Temurin Alpine 이미지의 런타임 호환성 확인 (JDK에서 JRE로 변경)
    • version.txt: 버전 관리 전략 및 워크플로우와의 동기화 메커니즘 확인

시 🐰

Docker 이미지 춤을 추며,
버전이 마법처럼 흘러가고,
알파인 산 위에서 빌드와 배포가 만난다—
자동화라는 이름의 아름다운 여행! 🚀✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 템플릿 구조를 따르지만, 실제 내용이 템플릿 플레이스홀더일 뿐 구체적인 정보가 채워지지 않았습니다. 개요, 작업 사항, 참고 사항 섹션이 비어있어 실질적인 설명이 부족합니다. 개요, 작업 사항, 참고 사항 섹션을 실제 내용으로 채워주세요. CI/CD 구현 내용, 버전 관리 방식, Docker 이미지 변경 사유 등을 구체적으로 작성해야 합니다.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 GitHub Actions 기반 CI/CD 테스트라는 변경의 핵심을 명확하게 요약하고 있으며, 추가된 워크플로우, Dockerfile 변경, version.txt 파일과 관련된 주요 변경사항을 충분히 반영합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7dd3085 and 05a9af2.

📒 Files selected for processing (3)
  • .github/workflows/deploy-test.yml (1 hunks)
  • Dockerfile (1 hunks)
  • version.txt (1 hunks)
🧰 Additional context used
🪛 Checkov (3.2.334)
Dockerfile

[low] 1-3: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-3: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🪛 YAMLlint (1.37.1)
.github/workflows/deploy-test.yml

[warning] 3-3: truthy value should be one of [false, true]

(truthy)


[error] 5-5: too many spaces inside brackets

(brackets)


[error] 5-5: too many spaces inside brackets

(brackets)


[error] 7-7: too many spaces inside brackets

(brackets)


[error] 7-7: too many spaces inside brackets

(brackets)

🔇 Additional comments (1)
Dockerfile (1)

2-2: JAR 파일명 검증 완료 - 리뷰 코멘트 취소

빌드 설정과 Dockerfile의 JAR 파일명이 이미 일치합니다.

검증 결과:

  • settings.gradle: rootProject.name = 'SearchWeb'
  • build.gradle: version = '0.0.1-SNAPSHOT', jar { enabled = false }
  • Spring Boot 3.3.1의 bootJar 태스크가 기본 설정으로 생성할 JAR: SearchWeb-0.0.1-SNAPSHOT.jar
  • Dockerfile 라인 2: COPY build/libs/SearchWeb-0.0.1-SNAPSHOT.jar app.jar

JAR 파일명이 정확하게 일치하므로 추가 작업이 필요하지 않습니다.

Likely an incorrect or invalid review comment.

Comment on lines +3 to +8
on:
push:
branches: [ "main", "dev" ]
pull_request:
branches: [ "main", "dev" ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

YAML 형식 오류를 수정하세요: 'on' 키와 배열 괄호 간격.

YAMLlint에서 다음 오류를 보고했습니다:

  • Line 3: on: 값이 boolean처럼 보임
  • Lines 5, 7: 배열 괄호 간격 오류

다음과 같이 수정하세요:

-on:
+on:
-    branches: [ "main", "dev" ]
+    branches: ["main", "dev"]

또는 더 명확하게:

-on:
+on: &defaults

권장 YAML 형식:

-on:
+on:
   push:
-    branches: [ "main", "dev" ]
+    branches: ["main", "dev"]
   pull_request:
-    branches: [ "main", "dev" ]
+    branches: ["main", "dev"]
🧰 Tools
🪛 YAMLlint (1.37.1)

[warning] 3-3: truthy value should be one of [false, true]

(truthy)


[error] 5-5: too many spaces inside brackets

(brackets)


[error] 5-5: too many spaces inside brackets

(brackets)


[error] 7-7: too many spaces inside brackets

(brackets)


[error] 7-7: too many spaces inside brackets

(brackets)

🤖 Prompt for AI Agents
.github/workflows/deploy-test.yml around lines 3 to 8: the 'on' mapping and
branch arrays are incorrectly formatted causing YAMLlint errors; fix by ensuring
'push' and 'pull_request' are indented under 'on' and replace the inline array
with a proper block list (branches: - main - dev) or, if you prefer inline, use
correct flow style with no extra spaces inside brackets (branches:
["main","dev"]) so the file parses as a mapping with properly formatted arrays.

Comment on lines +21 to +25
- name: Read version.txt
id: meta
run: |
VERSION=$(cat version.txt | tr -d ' \n')
echo "IMAGE_TAG=${VERSION}" >> $GITHUB_OUTPUT
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

version.txt 읽기 방식을 개선하세요. 현재 방식도 작동하나 더 명확한 방법이 있습니다.

현재 tr -d ' \n' 사용도 작동하지만, 더 robust한 방식을 추천합니다:

       - name: Read version.txt
         id: meta
         run: |
-          VERSION=$(cat version.txt | tr -d ' \n')
-          echo "IMAGE_TAG=${VERSION}" >> $GITHUB_OUTPUT
+          VERSION=$(head -1 version.txt | xargs)
+          if [ -z "$VERSION" ]; then
+            echo "❌ version.txt is empty"
+            exit 1
+          fi
+          echo "IMAGE_TAG=${VERSION}" >> $GITHUB_OUTPUT
+          echo "✅ Image tag: ${VERSION}"

또는 더 간단하게:

VERSION=$(grep . version.txt | head -1 | xargs)

장점:

  • xargs: 공백 및 줄바꿈 자동 제거
  • 빈 파일 검증 추가
  • 로그에 버전 출력으로 디버깅 용이
🤖 Prompt for AI Agents
.github/workflows/deploy-test.yml around lines 21 to 25: the current command
reads version.txt with cat and strips whitespace via tr which works but is less
robust; update the step to read the first non-empty line, trim surrounding
whitespace, fail early if the file is empty, and echo the resolved version to
the workflow logs and to GITHUB_OUTPUT; implement this by reading the first
non-empty line (e.g., using grep/head or a shell read loop), trim with xargs or
parameter expansion, test for empty string and exit non-zero with a clear
message if empty, then set IMAGE_TAG and echo the version for debugging.

Comment on lines +45 to +55
- name: Docker Build (version + latest)
run: |
docker build \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
.

- name: Docker Push
run: |
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

빌드 및 푸시 단계의 에러 핸들링을 강화하세요. 이미지 푸시 실패 시에도 배포가 진행됩니다.

현재는 docker build/push 실패 시에도 배포 job이 실행될 수 있습니다. needs 의존성이 있으나, build/push 단계 내 에러 핸들링이 부족합니다.

다음과 같이 개선하세요:

       - name: Docker Build (version + latest)
         run: |
           docker build \
             -t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
             -t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
             .
+          if [ $? -ne 0 ]; then
+            echo "❌ Docker build failed"
+            exit 1
+          fi
 
       - name: Docker Push
         run: |
           docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
-          docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
+          if [ $? -ne 0 ]; then
+            echo "❌ Failed to push versioned image"
+            exit 1
+          fi
+          
+          docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
+          if [ $? -ne 0 ]; then
+            echo "❌ Failed to push latest image"
+            exit 1
+          fi

또는 set -e 사용:

       - name: Docker Build (version + latest)
+        shell: bash
         run: |
+          set -e
           docker build \
             -t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
             -t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
             .
 
       - name: Docker Push
+        shell: bash
         run: |
+          set -e
           docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
           docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Docker Build (version + latest)
run: |
docker build \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
.
- name: Docker Push
run: |
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
- name: Docker Build (version + latest)
run: |
docker build \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
.
if [ $? -ne 0 ]; then
echo "❌ Docker build failed"
exit 1
fi
- name: Docker Push
run: |
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
if [ $? -ne 0 ]; then
echo "❌ Failed to push versioned image"
exit 1
fi
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
if [ $? -ne 0 ]; then
echo "❌ Failed to push latest image"
exit 1
fi
Suggested change
- name: Docker Build (version + latest)
run: |
docker build \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
.
- name: Docker Push
run: |
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
- name: Docker Build (version + latest)
shell: bash
run: |
set -e
docker build \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }} \
-t ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest \
.
- name: Docker Push
shell: bash
run: |
set -e
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${{ steps.meta.outputs.IMAGE_TAG }}
docker push ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
🤖 Prompt for AI Agents
.github/workflows/deploy-test.yml around lines 45 to 55: the Docker build/push
steps lack robust error handling so failures can be ignored and downstream
deploy can still run; modify the steps so the shell fails fast (e.g., enable
"set -e" at the top of the run block or explicitly check each command's exit
status and exit non‑zero on failure) or configure the job/step to not
continue-on-error and to fail the workflow when build or push fails, ensuring
the deploy job does not run if image creation or push fails.

Comment on lines +66 to +73
- name: Prepare SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa

- name: Add Known Hosts
run: ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🔒 SSH 키 보안: ssh-agent를 사용하세요. 현재 방식은 로그 노출 위험이 있습니다.

Line 69에서 SSH 키를 직접 파일에 작성하는 것은 다음 위험이 있습니다:

  • 워크플로우 디버그 로그에서 키가 노출될 수 있음
  • 임시 파일이 안전하지 않을 수 있음

ssh-agent를 사용하여 개선하세요:

-      - name: Prepare SSH key
-        run: |
-          mkdir -p ~/.ssh
-          echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
-          chmod 600 ~/.ssh/id_rsa
+      - name: Setup SSH
+        uses: webfactory/ssh-agent@v0.8.0
+        with:
+          ssh-private-key: ${{ secrets.SSH_KEY }}

그리고 다음 단계를 수정하세요:

-      - name: Add Known Hosts
-        run: ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
+      - name: Add SSH Host Key
+        run: |
+          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
+          chmod 644 ~/.ssh/known_hosts
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Prepare SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
- name: Add Known Hosts
run: ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Setup SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_KEY }}
- name: Add SSH Host Key
run: |
ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
🤖 Prompt for AI Agents
.github/workflows/deploy-test.yml lines 66-73: the workflow writes the SSH
private key directly to ~/.ssh/id_rsa (exposing it in logs and as a temporary
file); replace that block to use ssh-agent instead — invoke the official
actions/ssh-agent with the private key from secrets (so it loads the key into
the agent rather than echoing to disk), ensure PUBLIC/known_hosts is populated
using ssh-keyscan (or use the ssh-agent action's known_hosts input if
available), and remove the echo/chmod steps; keep the Known Hosts step but run
it after the agent is started if you still need to append to ~/.ssh/known_hosts.

Comment on lines +75 to +90
- name: Deploy via SSH
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF
docker login -u '${{ secrets.DOCKERHUB_USERNAME }}' -p '${{ secrets.DOCKERHUB_TOKEN }}'
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${IMAGE_TAG}
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest

if [ "$(docker ps -q -f name=searchweb)" ]; then docker stop searchweb; fi
if [ "$(docker ps -aq -f name=searchweb)" ]; then docker rm searchweb; fi

docker run -d \
--env-file /home/ubuntu/searchweb/.env \
-p 8080:8080 \
--name searchweb \
${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
EOF
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🔐 Docker 자격증명을 SSH 명령어에 직접 포함하지 마세요. 로그에서 평문 비밀번호가 노출될 수 있습니다.

Line 78에서 docker login 명령어에 비밀번호를 직접 전달하면:

  • GitHub Actions 로그에 평문으로 노출
  • 환경 변수로 마스킹되지 않음

원격 호스트에서 Docker 자격증명을 안전하게 관리하세요:

      - name: Deploy via SSH
        run: |
          ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF
-            docker login -u '${{ secrets.DOCKERHUB_USERNAME }}' -p '${{ secrets.DOCKERHUB_TOKEN }}'
-            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${IMAGE_TAG}
-            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
+            # 원격 호스트에 미리 설정된 credentials helper 사용
+            # 또는 ~/.docker/config.json에 base64 인코딩된 자격증명 저장
+            if [ ! -f /home/ubuntu/.docker/config.json ]; then
+              echo '${{ secrets.DOCKER_CONFIG }}' | base64 -d > /home/ubuntu/.docker/config.json
+              chmod 600 /home/ubuntu/.docker/config.json
+            fi
+            
+            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${IMAGE_TAG}
+            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
             
             if [ "$(docker ps -q -f name=searchweb)" ]; then docker stop searchweb; fi
             if [ "$(docker ps -aq -f name=searchweb)" ]; then docker rm searchweb; fi
             
             docker run -d \
               --env-file /home/ubuntu/searchweb/.env \
               -p 8080:8080 \
               --name searchweb \
               ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
          EOF

권장 사항:

  1. DOCKER_CONFIG 시크릿으로 base64 인코딩된 Docker config 저장
  2. 원격 호스트에 credential helper (pass, secretservice 등) 설정
  3. 또는 Private Docker Registry 사용 (자체 인증 관리)
🤖 Prompt for AI Agents
.github/workflows/deploy-test.yml lines 75-90: the workflow currently runs
`docker login` with the password on the SSH command line which exposes
plain-text credentials in logs; replace that pattern by provisioning Docker
credentials on the remote host securely (do one of the following): push a
base64-encoded Docker config json into a DOCKER_CONFIG secret and on the remote
decode it to ~/.docker/config.json before pulling images, or use docker
credential helpers on the remote, or use `docker login --username` with
`--password-stdin` and provide the token via a masked stdin stream from the
action/secret (never embed the token directly in the SSH here); remove the
inline `-p '${{ secrets.DOCKERHUB_TOKEN }}'` and ensure the chosen method writes
credentials to the remote filesystem or credential helper prior to docker pull.

🧹 Nitpick | 🔵 Trivial

배포 단계에서 컨테이너 시작 실패 시 롤백 로직과 .env 파일 검증이 필요합니다.

현재 배포 script의 문제점:

  1. .env 파일 존재 여부 확인 없음 → 컨테이너 시작 실패
  2. 컨테이너 시작 실패 시 이전 버전으로 롤백 없음
  3. 새 컨테이너 생성 실패 시 알림 없음

개선된 배포 script:

       - name: Deploy via SSH
         run: |
           ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'EOFSCRIPT'
+          set -e
+          trap 'echo "❌ Deployment failed"; exit 1' ERR
+          
+          # .env 파일 검증
+          if [ ! -f /home/ubuntu/searchweb/.env ]; then
+            echo "❌ Error: /home/ubuntu/searchweb/.env not found"
+            exit 1
+          fi
+          
             docker login -u '${{ secrets.DOCKERHUB_USERNAME }}' -p '${{ secrets.DOCKERHUB_TOKEN }}'
             docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:${IMAGE_TAG}
             docker pull ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest
 
+            # 기존 컨테이너 백업
+            BACKUP_IMAGE=""
+            if [ "$(docker ps -q -f name=searchweb)" ]; then
+              BACKUP_IMAGE=$(docker inspect --format='{{.Image}}' searchweb 2>/dev/null || echo "")
+              docker stop searchweb
+            fi
             if [ "$(docker ps -aq -f name=searchweb)" ]; then
               docker rm searchweb
             fi
 
+            # 새 컨테이너 시작
             docker run -d \
               --env-file /home/ubuntu/searchweb/.env \
               -p 8080:8080 \
               --name searchweb \
               ${{ secrets.DOCKERHUB_USERNAME }}/searchweb:latest || {
+                echo "❌ Failed to start new container"
+                if [ -n "$BACKUP_IMAGE" ]; then
+                  echo "🔄 Attempting rollback to: $BACKUP_IMAGE"
+                  docker run -d \
+                    --env-file /home/ubuntu/searchweb/.env \
+                    -p 8080:8080 \
+                    --name searchweb \
+                    "$BACKUP_IMAGE"
+                fi
+                exit 1
+              }
+          
+            echo "✅ Deployment successful"
-          EOF
+          EOFSCRIPT
🤖 Prompt for AI Agents
In .github/workflows/deploy-test.yml around lines 75 to 90, the deploy SSH block
lacks checks for the .env file, no verification that the new container started
successfully, and no rollback or notification on failure; update the script to
(1) verify the remote .env exists and contains required keys before
pulling/running the image, (2) before stopping the current container save the
current image tag or create a backup container (e.g., docker commit or docker
rename) so you can restore it, (3) after running the new container perform a
health check (container running + simple HTTP check or docker inspect for
HEALTH) and, if the health check fails, stop/remove the failed container and
revert to the saved previous container/image, and (4) surface failures by
exiting with a non-zero status and emitting a notification (echo to
stdout/stderr or call a webhook/GitHub Actions workflow command) so CI shows the
error. Ensure all steps run idempotently and handle missing backups gracefully.

@@ -1,3 +1,3 @@
FROM openjdk:17-jdk-slim
FROM eclipse-temurin:17-jre-alpine
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

베이스 이미지 변경은 좋은 선택입니다. 그러나 보안 및 운영성을 위해 USER와 HEALTHCHECK을 추가하세요.

eclipse-temurin:17-jre-alpine은 보안 업데이트가 정기적이고 이미지 크기가 작아 배포 성능이 개선됩니다. 그러나 컨테이너 보안과 운영성을 위해 다음 개선사항을 추천합니다:

  1. USER 지정: root 사용자로 실행하는 것은 보안 위험입니다.
  2. HEALTHCHECK 추가: 쿠버네티스나 컨테이너 오케스트레이션 환경에서 헬스 체크가 필요합니다.

다음 diff를 적용하세요:

-FROM eclipse-temurin:17-jre-alpine
+FROM eclipse-temurin:17-jre-alpine
+
+# Non-root user 생성
+RUN addgroup -S appgroup && adduser -S appuser -G appgroup
+
 COPY build/libs/SearchWeb-0.0.1-SNAPSHOT.jar app.jar
-
+
+# 헬스 체크 추가
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+  CMD java -jar app.jar || exit 1
+
+USER appuser
 ENTRYPOINT ["java", "-Dspring.profiles.active=docker", "-jar", "app.jar"]

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Checkov (3.2.334)

[low] 1-3: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)


[low] 1-3: Ensure that a user for the container has been created

(CKV_DOCKER_3)

🤖 Prompt for AI Agents
In Dockerfile around lines 1 to 1, the base image was changed but the container
runs as root and lacks a healthcheck; update the Dockerfile to (1) create and
switch to a non-root user and set USER to that account (e.g., add a group/user
with non-zero UID, chown necessary app dirs, and set USER), and (2) add a
HEALTHCHECK instruction that uses a lightweight command to verify the
application is responsive (adjust interval, timeout, retries) so orchestration
platforms can detect unhealthy containers; ensure the healthcheck command exits
with proper status codes and does not run as root-only.

@@ -0,0 +1 @@
1.1.0 No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

파일 끝에 줄바꿈을 추가하세요.

버전 형식(Semantic Versioning)은 좋으나, 파일 끝에 줄바꿈이 없으면 Git에서 경고가 발생합니다. 이는 Unix/POSIX 파일 표준입니다.

다음 diff를 적용하세요:

-1.1.0
\ No newline at end of file
+1.1.0

또는 명령어로 수정:

echo "" >> version.txt
🤖 Prompt for AI Agents
version.txt lines 1-1: 파일 끝에 개행(newline)이 누락되어 Git에서 경고가 발생합니다; 파일의 마지막 줄 뒤에 빈
줄(줄바꿈)을 추가하여 파일을 종료하도록 수정하세요 (직접 파일에 개행을 추가하거나 파일 끝에 한 줄 개행을 추가하는 명령을 실행하여 저장).

@jin2304 jin2304 merged commit d5c759c into dev Dec 8, 2025
2 checks passed
@jin2304 jin2304 linked an issue Dec 21, 2025 that may be closed by this pull request
@jin2304 jin2304 changed the title devops: GitHub Actions 기반 CI/CD 테스트 Devops/#12 GitHub Actions 기반 CI/CD 테스트 Jan 17, 2026
@jin2304 jin2304 deleted the devops/#12 branch February 1, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🌎 deploy 배포 관련 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DevOps] GitHub Actions 기반 CI/CD 테스트

1 participant